﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Windows.Threading;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.IO;
//using System.Drawing;

namespace udp_camera
{
    public static class JPEG
    {
        public static List<byte[]> entireJPEG = new List<byte[]>();

    }


    public class VidCamReceive
    {

        [DllImport("gdi32")]
        static extern int DeleteObject(IntPtr o);

        public static BitmapSource loadBitmap(System.Drawing.Bitmap source)
        {
            IntPtr ip = source.GetHbitmap();
            BitmapSource bs = null;
            try
            {
                bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip,
                   IntPtr.Zero, Int32Rect.Empty,
                   System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(ip);
            }

            return bs;
        }

        const int chunckSize = 500;
        static int totalJPEGsize;
        byte[] JPEGchuck = new byte[chunckSize];
        static int currentChuckNumber;
        static int ChuckNumber_expected = 0;
        static int numOfChucks;
        static int jpegNumber_expected;

        static BitmapImage BitmapToImageSource(System.Drawing.Bitmap bitmap)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Jpeg);
                memory.Position = 0;
                BitmapImage bitmapImage = new BitmapImage();
                bitmapImage.BeginInit();
                bitmapImage.StreamSource = memory;
                bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
                bitmapImage.EndInit();

                return bitmapImage;
            }
        }

        public static System.Windows.Media.ImageSource ConvertDrawingImage2MediaImageSource(System.Drawing.Image image)
        {
            using (var ms = new MemoryStream())
            {
                var bitmap = new System.Windows.Media.Imaging.BitmapImage();
                bitmap.BeginInit();

                image.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                ms.Seek(0, System.IO.SeekOrigin.Begin);
                bitmap.StreamSource = ms;
                bitmap.EndInit();
                return bitmap;
            }
        }

        public static void receive(byte[] data)
        {
            totalJPEGsize = BitConverter.ToInt32(data, 4);

            //Gathers all chucks forming a JPEG.
            //If a chuck is missing, entire JPEG is invalid
            int jpegNumber = BitConverter.ToInt32(data, 0);
            currentChuckNumber = BitConverter.ToInt32(data, 8);

            //first chuck of the JPEG? new JPEG time!
            if (currentChuckNumber == 0)
            {

                jpegNumber_expected = jpegNumber;
                ChuckNumber_expected = 0;
                totalJPEGsize = BitConverter.ToInt32(data, 4);
                numOfChucks = (totalJPEGsize / chunckSize) + 1;

            }
            else if (jpegNumber_expected != jpegNumber)
            {
                return;
            }

            //is it the chunck we expect to get?
            if (currentChuckNumber == ChuckNumber_expected)
            {
                byte[] jpegByteArray = new byte[data.Length - 12];
                Array.Copy(data, 12, jpegByteArray, 0, data.Length - 12);
                JPEG.entireJPEG.Add(jpegByteArray);

                //last chunck means we're finishd 
                if ((numOfChucks - 1) == currentChuckNumber)
                {
                    
                    byte[] retrievedBytes = new byte[(JPEG.entireJPEG.Count - 1) * chunckSize + JPEG.entireJPEG[JPEG.entireJPEG.Count - 1].Length];
                    retrievedBytes = JPEG.entireJPEG.SelectMany(a => a).ToArray();
                   
                    ImageSourceConverter c = new ImageSourceConverter();


                    System.Drawing.Bitmap tempbit = (System.Drawing.Bitmap)((new System.Drawing.ImageConverter()).ConvertFrom(retrievedBytes));

                    BitmapSource tempSource = loadBitmap(tempbit);
                    tempSource.Freeze();

                    Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => MainWindow.image1_.Source = tempSource));
                    JPEG.entireJPEG.Clear();      
                }


                ChuckNumber_expected++;
            }
            //if not.. we'll have to wait until the next JPEG
            else
            {
                ChuckNumber_expected = -1;
                return;
            }
        }




    }

    public class UdpImageServer
    {
        private int port;
        private UdpClient udpServer;
        private IPEndPoint udp_ep;
        
        
        ASCIIEncoding ascii;
        

        public UdpImageServer(int port)
        {
            this.port = port;
            
        }

        public void startListening()
        {
            byte[] data = new byte[512];
            udp_ep = new IPEndPoint(IPAddress.Any, this.port);
            udpServer = new UdpClient(this.port);
            while (true)
            {
                data = udpServer.Receive(ref udp_ep);
                VidCamReceive.receive(data);
            }
        }
    }
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static Image image1_;
        public MainWindow()
        {
            InitializeComponent();
            Console.WriteLine("Listening port kthx");
            int port = int.Parse(Console.ReadLine());



            image1_ = this.image1;


            UdpImageServer udpServer = new UdpImageServer(port);
            Thread udpServerThread = new Thread(new ThreadStart(udpServer.startListening));
            udpServerThread.Start();
            
        }

    }
}
